import json
import os
import struct
import crcmod.predefined

class FrameParser:
    """
    根据提供的配置文件解析数据帧
    """
    def __init__(self, config):
        self.config = config
        self.frame_name = config.get("frame_name", "Unnamed Frame")
        self.frame_length = config.get("frame_length")
        self.fields = config.get("fields", [])
        self.validation_config = config.get("validation", {})

    def parse(self, data):
        """
        解析给定的字节数据
        :param data: bytes, 输入的原始数据
        :return: dict, 解析结果的字典, 包含 'data' 和 'validation_result'
        """
        if len(data) < self.frame_length:
            return None, "数据长度不足"

        # 截取一帧数据
        frame_data = data[:self.frame_length]

        parsed_values = {}
        for field in self.fields:
            name = field["name"]
            offset = field["byte_offset"]
            length = field["length"]
            field_type = field.get("type", "hex")
            endian = field.get("endian", "big") # 默认大端

            field_bytes = frame_data[offset : offset + length]
            if not field_bytes:
                continue
            
            value = self._unpack_value(field_bytes, field_type, endian)

            if "scale" in field:
                value *= field["scale"]

            unit = field.get("unit", "")
            parsed_values[name] = f"{value}{unit}" if unit else value
        
        validation_result = self._validate_frame(frame_data)

        return {
            "protocol_name": self.frame_name,
            "parsed_data": parsed_values,
            "validation": validation_result
        }, None

    def _unpack_value(self, field_bytes, field_type, endian):
        """
        根据类型解包数据
        """
        length = len(field_bytes)
        endian_char = '>' if endian == 'big' else '<'

        if field_type == "uint":
            return int.from_bytes(field_bytes, byteorder=endian)
        elif field_type == "int":
            return int.from_bytes(field_bytes, byteorder=endian, signed=True)
        elif field_type == "float":
            if length == 4:
                return struct.unpack(f'{endian_char}f', field_bytes)[0]
            elif length == 8:
                return struct.unpack(f'{endian_char}d', field_bytes)[0]
        elif field_type == "hex":
            return field_bytes.hex().upper()
        elif field_type == "string":
            return field_bytes.decode('utf-8', errors='ignore')
        
        return field_bytes.hex().upper() # 默认返回hex

    def _validate_frame(self, frame_data):
        """
        执行帧校验
        """
        if not self.validation_config or "checksum" not in self.validation_config:
            return "无校验配置"

        checksum_config = self.validation_config["checksum"]
        calc_range = checksum_config["calculation_range"]
        verify_field_name = checksum_config["verify_field"]
        
        # 找到校验值字段的字节
        verify_field_info = next((f for f in self.fields if f["name"] == verify_field_name), None)
        if not verify_field_info:
            return f"校验失败: 未找到名为'{verify_field_name}'的校验字段"
        
        offset = verify_field_info["byte_offset"]
        length = verify_field_info["length"]
        expected_checksum_bytes = frame_data[offset : offset + length]

        # 获取用于计算校验的数据
        data_to_check = frame_data[calc_range[0] : calc_range[1] + 1]

        # 计算实际校验值
        checksum_type = checksum_config["type"]
        try:
            if checksum_type == "crc16-modbus":
                crc16 = crcmod.predefined.mkCrcFun('modbus')
                calculated_checksum = crc16(data_to_check)
                calculated_checksum_bytes = calculated_checksum.to_bytes(2, byteorder='little') # Modbus CRC是小端
            elif checksum_type == "sum":
                calculated_checksum = sum(data_to_check) & 0xFF
                calculated_checksum_bytes = calculated_checksum.to_bytes(1, byteorder='big')
            else:
                return f"不支持的校验类型: {checksum_type}"

            if calculated_checksum_bytes == expected_checksum_bytes:
                return f"成功 ({checksum_type})"
            else:
                return f"失败 ({checksum_type}): 期望值={expected_checksum_bytes.hex().upper()}, 计算值={calculated_checksum_bytes.hex().upper()}"

        except Exception as e:
            return f"校验计算出错: {e}"
        
        return "未知校验错误"


def get_available_configs(config_dir="configs"):
    """
    从指定目录加载所有 .json 配置文件
    :param config_dir: str, 存放配置文件的目录
    :return: list, 包含所有配置字典的列表
    """
    configs = []
    if not os.path.isdir(config_dir):
        return configs
        
    for filename in os.listdir(config_dir):
        if filename.endswith(".json"):
            filepath = os.path.join(config_dir, filename)
            try:
                with open(filepath, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    config['__filename__'] = filename # 保存原始文件名
                    configs.append(config)
            except Exception as e:
                print(f"Error loading config {filename}: {e}")
    return configs 